//============================================================================================
/**
 * @file	throw_ai.c
 * @brief	玉投げAI
 * @author	Nozomu Saito
 *
 * モジュール名：ThrowAI_
 */
//============================================================================================

#include "throw_ai.h"
#include "throw_comm.h"
#include "throw_calc.h"

#define POKE_NUM	(ENTRY_MEMBER_NUM_MAX*ENTRY_MAX)
#define BALL_NUM	(ENTRY_MEMBER_NUM_MAX*ENTRY_MAX)
#define NODE_MAX	( (ENTRY_MEMBER_NUM_MAX-1)*ENTRY_MAX )

#define EXE_FRAME_ALONE	(40)			//非通信時AI実行フレーム間隔
#define EXE_FRAME_COMM	(40)			//通信時AI実行フレーム間隔

#define BALL_BIG_HP	(120)

#ifdef PM_DEBUG

///#define DBG_AI		//同じところを指定

#endif

typedef struct TH_AI_INFO_tag
{
	THROW_POKE *Poke;
	u16 PokeIdx;
	u16 Grading;
}TH_AI_INFO;

typedef  struct INFO_NODE_tag INFO_NODE;

typedef struct INFO_NODE_tag
{
	INFO_NODE *Prev;
	INFO_NODE *Next;
	TH_AI_INFO *Info;
}INFO_NODE;

typedef struct THROW_AI_CONT_tag
{
	THROW_POKE *Poke[POKE_NUM];
	THROW_BALL *Ball[BALL_NUM];
	TH_AI_INFO	Info[POKE_NUM];

	BOOL Execute;
	u8 NpcNum;	//0〜3
	u8 ExeNpcIdx;	//0〜2
	u8 Frame;
	u8 CntFrame;
	u8 NpcStartIdx;	//1〜3
	u8 PlayerNum;	//1〜4
	u8 ExePokeIdx;
	u8 dummy;
}THROW_AI_CONT;

static u8 SortInfo(const u8 inMyNetID, TH_AI_INFO **inInfo, TH_AI_INFO **outInfo);
static void ExecuteAI(THROW_AI_CONT_PTR ptr, const u8 inMyNetID, const u8  inTarget, const int inTime);

//----------------------------------------------------------------------------
/**
 *	@brief	初期化
 *
 *	@param	inHeapID		ヒープID
 *	@param	inNpcNum		NPCの数
 *	@param	inType			ゲームタイプ（通信してるか）
 *
 *	@return ptr				コントロールポインタ
 */
//-----------------------------------------------------------------------------
THROW_AI_CONT_PTR ThrowAI_Init(const int inHeapID, const int inNpcNum, const PKTHLN_GAME_TYPE inType)
{
	THROW_AI_CONT_PTR cnt_ptr;
	//メモリアロケーション
	int size = sizeof(THROW_AI_CONT);
	cnt_ptr = sys_AllocMemory(inHeapID, size);
	MI_CpuClear8( cnt_ptr, size);

	cnt_ptr->NpcNum = inNpcNum;
	cnt_ptr->PlayerNum = ENTRY_MEMBER_NUM_MAX-inNpcNum;
	cnt_ptr->NpcStartIdx = cnt_ptr->PlayerNum;
	if (inType == PKTHLN_GAME_ALONE){
		cnt_ptr->CntFrame = EXE_FRAME_ALONE;
	}else{
		cnt_ptr->CntFrame = EXE_FRAME_COMM;
	}
	cnt_ptr->Frame = cnt_ptr->CntFrame;	//すぐに起動するようにしておく
	return cnt_ptr;
}


//----------------------------------------------------------------------------
/**
 *	@brief	ポケモンデータのセットアップ
 *
 *	@param	ptr				コントロールポインタ
 *	@param	inIdx			プレーヤーインデックス
 *	@param	*ioThrowPoke	ポケモンデータへのポインタ
 *	@param  *ioBall			玉データへのポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void ThrowAI_SetupInfo(	THROW_AI_CONT_PTR ptr,
							const u8 inIdx,
							THROW_POKE *ioThrowPoke,
							THROW_BALL *ioBall)
{
	ptr->Poke[inIdx] = ioThrowPoke;
	ptr->Ball[inIdx] = ioBall;
}


//----------------------------------------------------------------------------
/**
 *	@brief	解放
 *
 *	@param	ptr				コントロールポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void ThrowAI_End(THROW_AI_CONT_PTR ptr)
{
	//メモリ解放
	sys_FreeMemoryEz(ptr);
}


//----------------------------------------------------------------------------
/**
 *	@brief	ＡＩ実行

 *	@param	ptr			管理ポインタ
 *	@param	inTime		経過時間
 *
 *	@return u8		動かしたＮＰＣインデックス
 */
//-----------------------------------------------------------------------------
u8 ThrowAI_Execute(THROW_AI_CONT_PTR ptr, const int inTime)
{
	u8 exe_idx;
	exe_idx = POKE_NUM;

	if (ptr->PlayerNum == ENTRY_MEMBER_NUM_MAX){
		//AIなし
		return 0;
	}

	ptr->Frame++;

	if (ptr->Frame >= ptr->CntFrame){
		//実行状態にする
		ptr->Execute = TRUE;
		ptr->Frame = 0;
	}

	if (ptr->Execute){	//実行状態
		int exe_poke;
		int my_net_id = ptr->NpcStartIdx + ptr->ExeNpcIdx;
/**
		if (my_net_id != 1){
			//実行NPCのシフト
			ptr->ExeNpcIdx++;
			if (ptr->ExeNpcIdx >= ptr->NpcNum){	//シフト後一巡したら、
				ptr->ExeNpcIdx = 0;
				//実行するポケモンインデックスを変更する
				ptr->ExePokeIdx = (ptr->ExePokeIdx+1) % ENTRY_MAX;
				//実行フラグを落とす
				ptr->Execute = FALSE;
			}
			return exe_idx;
		}
*/
		//実行NPCのポケ決定
		exe_poke = (my_net_id * ENTRY_MAX) + ptr->ExePokeIdx;
		GF_ASSERT(exe_poke >= ptr->NpcStartIdx );
		//ボールが出せるなら実行
		if ( ( (ptr->Ball[exe_poke]->State == BALL_ST_NONE)||
					(ptr->Ball[exe_poke]->State == BALL_ST_SET) )&&
				(ptr->Poke[exe_poke]->State != POKE_ST_PANIC) ){
			ExecuteAI(ptr, my_net_id, exe_poke, inTime);
		}else {
			OS_Printf("ボールがまだ出せない\n");
		}
		exe_idx = exe_poke;
		//実行NPCのシフト
		ptr->ExeNpcIdx++;
		if (ptr->ExeNpcIdx >= ptr->NpcNum){	//シフト後一巡したら、
			ptr->ExeNpcIdx = 0;
			//実行するポケモンインデックスを変更する
			ptr->ExePokeIdx = (ptr->ExePokeIdx+1) % ENTRY_MAX;
			//実行フラグを落とす
			ptr->Execute = FALSE;
		}
	}
	return exe_idx;
}

//----------------------------------------------------------------------------
/**
 *	@brief		AI実行
 *
 *	@param	ptr				コントローラポインタ
 *	@param	inMyNetID		ネットID
 *	@param	inTarget		対象ポケインデックス
 *	@param	inTime			経過時間
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void ExecuteAI(THROW_AI_CONT_PTR ptr, const u8 inMyNetID, const u8  inTarget, const int inTime)
{
	int i;
	TH_AI_INFO *src_info[POKE_NUM];
	TH_AI_INFO *dst_info[NODE_MAX];
	u8 num;
	u8 idx;

	//玉を大きくするかを決める
	if ( BALL_BIG_HP >= ptr->Poke[inTarget]->Hp + (gf_rand()%51)&&
			ptr->Ball[inTarget]->Level < LEVEL_MAX){
		//玉を大きくする
		if (ptr->Ball[inTarget]->State == BALL_ST_NONE){
			ptr->Ball[inTarget]->Level = 1;
			ptr->Ball[inTarget]->State = BALL_ST_SET;
			//ボール初期位置セット
			ptr->Ball[inTarget]->Pos = ChkPokePos[inTarget];
		}else{
			GF_ASSERT(ptr->Ball[inTarget]->State == BALL_ST_SET);
			ptr->Ball[inTarget]->Level++;
			if (ptr->Ball[inTarget]->Level <= LV_FST){
				ptr->Ball[inTarget]->Size = 0;
			}else if(ptr->Ball[inTarget]->Level <= LV_2ND){
				ptr->Ball[inTarget]->Size = 1;
			}else if(ptr->Ball[inTarget]->Level <= LV_3RD){
				ptr->Ball[inTarget]->Size = 2;
			}else{
				ptr->Ball[inTarget]->Size = 3;
			}
		}

		return;
	}

	for(i=0;i<POKE_NUM;i++){
		ptr->Info[i].Poke = ptr->Poke[i];
		ptr->Info[i].PokeIdx = i;
		//各ポケモンの得点+ランダム（0〜20）
		ptr->Info[i].Grading = (ptr->Poke[i]->Point/20) + (gf_rand()%21);
		//気絶チェックによる評価点加算
		if (ptr->Poke[i]->State == POKE_ST_PANIC){
			ptr->Info[i].Grading+=1;
		}
		//スタミナによる評価点加算
		ptr->Info[i].Grading += ( (HP_MAX-ptr->Poke[i]->Hp)/20 );
	}
	for(i=0;i<POKE_NUM;i++){
		src_info[i] = &ptr->Info[i];
	}
	//評点の高い順にソート（自ポケを省く）
	num = SortInfo(inMyNetID ,src_info, dst_info);
	//同点の場合はその中でランダム選出
	idx = gf_rand()%num;
	//ターゲットが決まったので、方向ベクトルを算出
	{
		float f_val;
		VecFx32 vec;
		VecFx32 add = {0,0,0};
		u8 dst_idx = dst_info[idx]->PokeIdx;
		//自分の位置から、対象ポケの位置へのベクトルを算出
		VEC_Subtract(&ChkPokePos[dst_idx], &ChkPokePos[inTarget], &vec);
		//正規化
		VEC_Normalize(&vec, &vec);
		//角度のぶれを計算
		ThClc_FixDir( 3, &vec );
		//テクニックによる方向変更
		ThClc_FixDir(ptr->Poke[inTarget]->Technique, &vec);

		//射出力を算出
		f_val = (float)ptr->Poke[inTarget]->Hp / 50.0 + (float)(gf_rand()%51) + ((float)inTime / 40.0);
		if (f_val > BALL_SPD_MAX){
			f_val = BALL_SPD_MAX;
		}else if(f_val <BALL_SPD_MIN){
			f_val = BALL_SPD_MIN;
		}

		OS_Printf("%dが%dに発射\n",inTarget,dst_idx);

		//方向ベクトルに乗算
		VEC_MultAdd(f_val*FX32_ONE, &vec, &add, &ptr->Ball[inTarget]->Dir);
		//ボール移動
		ptr->Ball[inTarget]->State = BALL_ST_MOVE;
		//ボール初期位置セット
		ptr->Ball[inTarget]->Pos = ChkPokePos[inTarget];
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	データソート
 *
 *	@param	inMyNetID	自分のネットID
 *	@param	*inInfo		ソート前データ
 *	@param	*outInfo	ソート後データ
 *
 *	@return	count		一番大きい値を持った要素数の個数
 */
//-----------------------------------------------------------------------------
static u8 SortInfo(const u8 inMyNetID, TH_AI_INFO **inInfo, TH_AI_INFO **outInfo)
{
	u8 count;
	u8 num;
	u8 i,j;
	INFO_NODE node[1+NODE_MAX];

	//準備
	node[0].Prev = &node[1];
	node[0].Next = &node[1];
	node[0].Info = NULL;

	count = 0;
	num = 0;
	for(i=0;i<POKE_NUM;i++){
		INFO_NODE *now;
		INFO_NODE *target;

		//自ポケは対象外
		if (i/ENTRY_MAX == inMyNetID){
			continue;
		}
		//ノード取得
		now = &node[num+1];
		//値セット
		now->Info = inInfo[i];
		now->Next = NULL;
		now->Prev = NULL;

		target = &node[0];
		for(j=0;j<num;j++){
			target = target->Next;
			//挿入
			if (target->Info->Grading < now->Info->Grading){	//値が大きい
				//リストの前に追加
				target->Prev->Next = now;
				now->Prev = target->Prev;
				now->Next = target;
				target->Prev = now;
				break;
			}
		}
		if (j==num){
			//リスト最後に追加
			target->Next = now;
			now->Prev = target;
		}
		num++;
	}

	//ソート終了したら、バッファにデータを格納
	{
		INFO_NODE *now;
		u8 check;
		u16 max_val;
		//最大値の取得
		max_val = node[0].Next->Info->Grading;
		now = &node[0];
		for(i=0;i<NODE_MAX;i++){
			now = now->Next;
			outInfo[i] = now->Info;
			//一番高い値がいくつあるかカウント
			if (max_val == outInfo[i]->Grading){
				count++;
			}
		}
	}

	GF_ASSERT(count > 0);

	return count;
}